#include <iostream>
#include <map>
#include <string>
#include <algorithm>
#include "Classifier.h"
#include "NaiveBayesClassifier.h"
#include "KeyMapper.h"
#include "KeypadKeyMapper.h"
#include "QwertyKeyMapper.h"
#include "SWPSUtil.h"
#include "Evaluation.h"
#include "BKTree.h"

using namespace std;

int main (int argc, char * const argv[])
{
    
    enum dataformat
    {
        swps = 0,
        switchboard,
        conll
    };
    const char* dataformatnames[] =
    {
        "swps",
        "switchboard",
        "conll"
    };
    
    dataformat format;
    string swpsfile = "";
    vector<string> trainingdata;
    string featuredata = "";
    string testdata = "";
    bool verbose = false;
    const char* current = "NULL";
    
    cout << "Parsing arguments\n";
    
    for (int i = 1; i < argc; i++)
    {
        if (!strcmp(argv[i], "SWPS"))
        {
            current = argv[i];
            continue;
        }
        if (!strcmp(argv[i], "DATAFORMAT"))
        {
            current = argv[i];
            continue;
        }
        if (!strcmp(argv[i], "TRAININGFILE"))
        {
            current = argv[i];
            continue;
        }
        if (!strcmp(argv[i], "TESTFILE"))
        {
            current = argv[i];
            continue;
        }
        if (!strcmp(argv[i], "FEATUREFILE"))
        {
            current = argv[i];
            continue;
        }
        if (!strcmp(argv[i], "VERBOSE"))
        {
            verbose = true;
            continue;
        }

        
        if (!strcmp(current, "SWPS"))
        {
            swpsfile = argv[i];
            continue;
        }
        if (!strcmp(current, "DATAFORMAT"))
        {
            if (!strcmp(argv[i], dataformatnames[swps])) 
            {
                format = swps;
                continue;
            }
            if (!strcmp(argv[i], dataformatnames[switchboard])) 
            {
                format = switchboard;
                continue;
            }
            if (!strcmp(argv[i], dataformatnames[conll])) 
            {
                format = conll;
                continue;
            }
        }
        if (!strcmp(current, "TRAININGFILE"))
        {
            trainingdata.push_back(argv[i]);
            continue;
        }
        if (!strcmp(current, "TESTFILE"))
        {
            testdata = argv[i];
            continue;
        }
        if (!strcmp(current, "FEATUREFILE"))
        {
            featuredata = argv[i];
            continue;
        }    }
    
    cout << "Initializing model\n";
    
    Classifier* nbc = new NaiveBayesClassifier();
    //TODO Make genere with argument and switch case
    //KeyMapper* keymapper = new KeypadKeyMapper();
    KeyMapper* keymapper = new QwertyKeyMapper();
    keymapper->setDistance(12);
    Evaluation* eval = new Evaluation();
    
    if (swpsfile == "")
    {
        return 2;
    }
    
    cout << "Reading SWPS format\n";
    SWPS swpsdef = SWPSUtil::ReadSWPS(swpsfile);
    vector< vector<string> > allfilesdata;
    vector< vector<string> > filedata;

    cout << "Training with " << trainingdata.size() << " training file(s)\n";
    for (int i = 0; i < trainingdata.size(); i++)
    {
        cout << "Training with " << trainingdata[i] << "\n";
        switch (format) {
            case swps:
                filedata = SWPSUtil::ReadSWPSData(trainingdata[i], swpsdef);
                nbc->TrainClassifier(filedata, swpsdef);
                allfilesdata.insert(allfilesdata.end(), filedata.begin(), filedata.end());
                break;
            case switchboard:
                filedata = SWPSUtil::ReadSWPSData(trainingdata[i], swpsdef);
                nbc->TrainClassifier(filedata, swpsdef);
                allfilesdata.insert(allfilesdata.end(), filedata.begin(), filedata.end());
                break;
            case conll:
                filedata = SWPSUtil::ReadSWPSData(trainingdata[i], swpsdef);
                nbc->TrainClassifier(filedata, swpsdef);
                allfilesdata.insert(allfilesdata.end(), filedata.begin(), filedata.end());
                break;
            default:
                cout << "SWPS format not valid\n";
                return 1;
                break;
        }
    }
        
    cout << "Building dictionaries\n";
    //nbc->SetFeatureData(SWPSUtil::ReadSWPSFeatureData(featuredata, swpsdef));
    nbc->BuildFeatureData(allfilesdata, swpsdef);
    nbc->BuildFeatureData(SWPSUtil::ReadSWPSData(featuredata, swpsdef), swpsdef);
    keymapper->BuildDictionary(nbc->GetTrainingData());
    keymapper->MapDictionary();    
    
    cout << "Build test data\n";
    switch (format) {
        case swps:
            eval->BuildTestData(SWPSUtil::ReadSWPSData(testdata, swpsdef), nbc->GetFeatureData(), swpsdef);
            break;
        case switchboard:
            eval->BuildTestData(SWPSUtil::ReadSwitchBoardData(testdata, swpsdef), nbc->GetFeatureData(), swpsdef);
            break;
        case conll:
            eval->BuildTestData(SWPSUtil::ReadCONLLData(testdata), nbc->GetFeatureData(), swpsdef);
            break;
        default:
            cout << "SWPS format not valid\n";
            return 1;
            break;
    }
    
    cout << "Evaluating model\n\n";
    eval->DisambiguatedAccuracy(nbc, keymapper, swpsdef, verbose);
    
    /*
    Classifier* nbc = new NaiveBayesClassifier();
    KeyMapper* keymapper = new KeypadKeyMapper();
    Instances trainingdata;
    trainingdata.n = 10;
    //instances.instances = new map<string, Instance>();
    
    Instance yes;
    yes.weight = 5;
    vector< map<string,double> > yesfeature(3);
    yesfeature[0].insert(pair<string, double>("Red", 3.0));
    yesfeature[0].insert(pair<string, double>("Yellow", 2.0));
    yesfeature[1].insert(pair<string, double>("Sports", 4.0));
    yesfeature[1].insert(pair<string, double>("SUV", 1.0));
    yesfeature[2].insert(pair<string, double>("Domestic", 2.0));
    yesfeature[2].insert(pair<string, double>("Imported", 3.0));
    yes.features = yesfeature;
    
    Instance no;
    no.weight = 5;
    vector< map<string,double> > nofeature(3);
    nofeature[0].insert(pair<string, double>("Red", 2.0));
    nofeature[0].insert(pair<string, double>("Yellow", 3.0));
    nofeature[1].insert(pair<string, double>("Sports", 2.0));
    nofeature[1].insert(pair<string, double>("SUV", 3.0));
    nofeature[2].insert(pair<string, double>("Domestic", 3.0));
    nofeature[2].insert(pair<string, double>("Imported", 2.0));
    no.features = nofeature;
    
    trainingdata.instances.insert(pair<string, Instance>("what", yes));
    trainingdata.instances.insert(pair<string, Instance>("that", no));
    
    vector<string> features(3);
    features[0] = "Red";
    features[1] = "SUV";
    features[2] = "Domestic";
    vector<string> words(2);
    words[0] = "what";
    words[1] = "that";
    
    nbc->SetTrainingData(trainingdata);
    keymapper->BuildDictionary(nbc->GetTrainingData());
    keymapper->MapDictionary(NULL);
    vector<string> mappedwords = keymapper->GetMappings("what");
    map<double, string> classifieds = nbc->Classify(mappedwords, features);

    for (map<double, string>::iterator it = classifieds.begin(); it != classifieds.end(); it++) 
    {
        cout << "p = " << (*it).first << " word = " << (*it).second << "\n";
    }
    */ 
    /*
    Classifier* nbcswps = new NaiveBayesClassifier();
    KeyMapper* keymapper = new KeypadKeyMapper();
    Evaluation* eval = new Evaluation();

    SWPS swps = SWPSUtil::ReadSWPS("/Users/rune/Documents/IT & Kognition/Speciale/src/swp/2.swps");
    
    vector< vector<string> > convtrainingdata = 
        SWPSUtil::ReadCONLLData("/Users/rune/Documents/IT & Kognition/Speciale/data/ontonotes4.0.enlish.bc.conll");
    
    vector< vector<string> > convtestdata = 
        SWPSUtil::ReadCONLLData("/Users/rune/Documents/IT & Kognition/Speciale/data/ontonotes4.0.enlish.bn.conll");

    //vector< vector<string> > sbtraining1data = SWPSUtil::ReadSwitchBoardData("/Users/rune/Documents/IT & Kognition/Speciale/src/swp/2.txt", swps);
    //vector< vector<string> > sbtraining2data = SWPSUtil::ReadSwitchBoardData("/Users/rune/Documents/IT & Kognition/Speciale/src/swp/3.txt", swps);
    //vector< vector<string> > sbtestdata = SWPSUtil::ReadSwitchBoardData("/Users/rune/Documents/IT & Kognition/Speciale/src/swp/4.txt", swps);
    //vector< vector<string> > exampletraining = SWPSUtil::ReadSWPSData("/Users/rune/Documents/IT & Kognition/Speciale/src/swp/example-training.swps", swps);
    //vector< vector<string> > exampletest = SWPSUtil::ReadSWPSData("/Users/rune/Documents/IT & Kognition/Speciale/src/swp/example-test.swps", swps);
    
    nbcswps->TrainClassifier(convtrainingdata, swps);
    nbcswps->TrainClassifier(convtestdata, swps);
    keymapper->BuildDictionary(nbcswps->GetTrainingData());
    keymapper->MapDictionary(NULL);
    cout << "feature count:  " << swps.featurecount << "\n";
    
    eval->BuildTestData(convtestdata, swps);
    eval->DisambiguatedAccuracy(nbcswps, keymapper);
    */
    
    return 0;
}
